/*
 * Copyright 2008-2010 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package jrebel.agent;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.instrument.Instrumentation;
import java.net.Socket;

import jrebel.comm.Command;
import jrebel.comm.ConfBody;
import jrebel.comm.ConfCommand;
import jrebel.comm.ErrorCommand;
import jrebel.comm.ExitCommand;
import jrebel.comm.OkayCommand;
import jrebel.comm.WireIO;
import jrebel.comm.ConfCommand.OPERATOR;


/**
 * Represents a remote client communicated by socket.
 * 
 * @author A. Sundararajan
 */
class RemoteClient extends Thread  implements CommandListener{
	private volatile Socket sock;
	private volatile ObjectInputStream ois;
	private volatile ObjectOutputStream oos;
	
	RemoteClient(Instrumentation inst, Socket sock) throws IOException {
		this.sock = sock;
		this.ois = new ObjectInputStream(sock.getInputStream());
		this.oos = new ObjectOutputStream(sock.getOutputStream());
	}
	public void handlerEvents() throws IOException {
		String ip = sock.getLocalAddress().getHostAddress();
		Main.debugPrint("client connect from " + ip );
		while(true ){
			Command cmd = WireIO.read(ois);
			onCommand(cmd) ;
		}
	}

	public void run(){
		try {
			handlerEvents() ;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			this.closeAll() ;
		}
	}
	protected boolean checkAuth(ConfBody conf) {
		Main.debugPrint(Main.getPassword()+"?="+conf.getPassword());
		return Main.getPassword().equals(conf.getPassword());
	}

	public void setConfigureForAgent(ConfBody body) {
		Main.debugPrint("set configure for the agent.");
		Main.setDebugMode(body.isDebugMode());
		Main.setDumpClasses(body.isDumpClasses());
		Main.setDumpDir(body.getDumpDir());
	}

	public void getConfigureFromAgent(ConfBody body) {
		Main.debugPrint("get configure for the agent.");
		body.setDebugMode(Main.isDebugMode());
		body.setDumpClasses(Main.isDumpClasses());
		body.setDumpDir(Main.getDumpDir());
	}

	public void onCommand(Command cmd) throws IOException {
		switch (cmd.getType()) {
		case Command.EXIT:
			WireIO.write(oos, cmd);
			break;
		case Command.CONF:
			handlerConfCommand((ConfCommand) cmd ) ;
		default:
			WireIO.write(oos, cmd);
		}
	}
	public void handlerConfCommand(ConfCommand cmd) throws IOException{
		ConfCommand conf = (ConfCommand)cmd ;
		ConfBody body = conf.getBody() ;
		if( ! checkAuth(body)&& conf.getOperator()!= OPERATOR.SETPASSWORD){
			WireIO.write(oos, new ErrorCommand(new Exception("Wrong password!!!")));
			return ;
		}
		switch(conf.getOperator()){
		case SETPASSWORD:
			Main.setPassword(body.getPassword());
			Main.debugPrint("client set password to ***"+body.getPassword());
			WireIO.write(oos, new OkayCommand());
			break;
		case RESETPASSWORD:
				Main.debugPrint("reset pasword");
				Main.setPassword(body.getNewPassword());
				WireIO.write(oos, new OkayCommand());
				break;
		case GETCONF:
				ConfBody rbody = new ConfBody();
				getConfigureFromAgent(rbody);
				ConfCommand r = new ConfCommand();
				r.setOperator(OPERATOR.GETCONF);
				r.setBody(rbody);
				WireIO.write(oos, r);
				break;
		case SETCONF:
				setConfigureForAgent(conf.getBody());
				WireIO.write(oos, new OkayCommand());
				break;
		case LOGIN:
			WireIO.write(oos, new OkayCommand());
			break;
		
		default:
			break;
		}
	}

	protected synchronized void closeAll() {
		if (oos != null) {
			try {
				oos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (ois != null) {
			try {
				ois.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (sock != null) {
			try {
				sock.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}